﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Security;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Diagnostics;
using CashResource;
using System.Windows.Forms;
using System.Drawing;

namespace CashCommon
{
    #region Сериализация

    /// <summary>
    /// Класс помогающий сохранять и извлекать данные сериализации 
    /// </summary>
    public sealed class WFSerialize
    {
        static private Stream NewFileStream(string fileName)
        {
            return File.Create(fileName);
        }
        static private Stream GetFileStream(string fileName)
        {
            if (File.Exists(fileName))
                return File.OpenRead(fileName);
            else
                return null;
        }
        static public void Serialize(string path, object value)
        {
            if (value == null)
                return;
            BinaryFormatter f = new BinaryFormatter();
            using (Stream nf = NewFileStream(path))
            {
                f.Serialize(nf, value);
            }
        }
        static public object Deserialize(string path)
        {
            BinaryFormatter f = new BinaryFormatter();
            using (Stream strm = GetFileStream(path))
            {
                if ((strm != null) && (strm.Length != 0))
                {
                    return f.Deserialize(strm);
                }
                else
                    return null;
            }
        }

        private WFSerialize() { }
    }

    /// <summary>
    /// Сохраняемый динамический список. 
    /// Предназначен для хранения списка элементов в файловой системе. Программы могут читать и изменять список через 
    /// механизм сериализации.
    /// Каждому элементу в списке соответствует некоторое число, что-то вроде "индекса цитируемости", его значение
    /// зависит от того, сколько раз элемент сохранялся в списке. При добавлении элемента в список, 
    /// проверяется его наличие в этом списке, и если элемент найден, то изменяется связанный с ним 
    /// "индекс цитируемости", если элемент не найден, то создается новый элемент, и с ним
    /// связывается "индекс цитируемости" минимального значения.                     
    /// При создании списка, можно указать его глубину - количество сохраняемых в списке элементов. 
    /// До тех пор пока количество размещенных в списке элементов меньше его глубины, новые элементы продолжают добавляються. 
    /// Когда же достигнута максимальная глубина, то новый элемент - добавляется, а один из старых - удаляется. 
    /// Удаляться будет тот, чей "индекс цитируемости" минимален.
    /// </summary>
    [Serializable]
    public class SaveList
    {
        // Constructors
        public SaveList() { }
        /// <summary>
        /// Конструктор задающий колличество поддерживаемых списком значений
        /// </summary>
        /// <param name="startItemCount">Кличество элементов поддерживаемых списком. По умолчанию 10</param>
        public SaveList(int startItemCount) : this()
        {
            this.startItemCount = startItemCount;
        }
        /// <summary>
        /// Конструтор задающий количество элементов поддерживаемых списком и максимальный индекс, при достижении
        /// которого, будет произведена реорганизация индекса.
        /// </summary>
        /// <param name="startItemCount">Число элементов списка</param>
        /// <param name="reorganizeSize">Максимальный размер индекса, по достижении которого будет произведена 
        /// реогранизация индекса</param>
        public SaveList(int startItemCount, int reorganizeSize) : this(startItemCount)
        {
            this.reorganizeSize = reorganizeSize;
        }

        // Interfaces 
        public void Add(SaveItem item)
        {
            findItem = item;
            SaveItem currItem = sl.Find(FindItem);
            if (currItem == null)
            {
                if (sl.Count == startItemCount)
                {
                    SaveItem min = sl.Min();
                    sl.Remove(min);
                }
                item.Count = 1;
                sl.Add(item);
            }
            else
            {
                currItem.Count++;

                // Реорганизация списка
                if (currItem.Count == reorganizeSize)
                    ReorganizeList();
            }
        }
        
        // Properties
        public ReadOnlyCollection<SaveItem> ListItems
        {
            get
            {
                return new ReadOnlyCollection<SaveItem>(sl);
            }
        }
        public int StartItemCount
        {
            get { return startItemCount; }
            set { startItemCount = value; }
        }
        public int ReorganizeSize
        {
            get { return reorganizeSize; }
            set { reorganizeSize = value; }
        }

        // Fields
        private List<SaveItem> sl = new List<SaveItem>();
        private SaveItem findItem;
        private int startItemCount = 10;
        private int reorganizeSize = 10000;

        // Private methods
        private bool FindItem(SaveItem si)
        {
            return si.Id == findItem.Id;
        }
        private void ReorganizeList()
        {
//            var orderedList = sl.Select(o => o).OrderBy(o => o.Count).Select(o => o.Count++);
            var orderedList = from f in sl
                              orderby (f.Count)
                              select f;
            int i = 0;
            foreach (SaveItem saveItem in orderedList)
                saveItem.Count = i++;
        }

    }
    
    [Serializable]
    public class SaveItem : IComparable
    {
        //Constructors
        public SaveItem() {}
        public SaveItem(int id, string name) : this()
        {
            this.Id = id;
            this.Name = name;
        }
        public SaveItem(int id, string name, string altName)
            : this(id, name)
        {
            this.AltName = altName;        
        }

        // Methods
        public int CompareTo(object obj)
        {
            SaveItem saveItem = obj as SaveItem;
            if (saveItem == null)
                throw new InvalidCastException();
            else
            {
                if (saveItem.Count > this.Count)
                    return -1;
                else if (saveItem.Count == this.Count)
                    return 0;
                else
                    return 1;
            }
        }
        public override bool Equals(object obj)
        {
            if (!(obj is SaveItem))
                return false;
            return (this.CompareTo(obj) == 0);
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        public static bool operator ==(SaveItem saveItem1, SaveItem saveItem2)
        {   
            if (object.ReferenceEquals(saveItem1, saveItem2))
                return true;
            if (object.ReferenceEquals(saveItem1, null))
                return false;
            return saveItem1.Equals(saveItem2);
        }
        public static bool operator !=(SaveItem saveItem1, SaveItem saveItem2)
        {
            return !(saveItem1 == saveItem2);
        }
        public static bool operator <(SaveItem saveItem1, SaveItem saveItem2)
        {
            return (saveItem1.CompareTo(saveItem2) < 0);
        }
        public static bool operator >(SaveItem saveItem1, SaveItem saveItem2)
        {
            return (saveItem1.CompareTo(saveItem2) > 0);
        }

        // Properties
        public int Id { get; set; }
        public string Name { get; set; }
        public string AltName { get; set; }
        public int Count { get; set; }
    }

    #endregion

    /// <summary>
    /// Вывод сообщения с поддержкой языков, где чтение происходит справо-налево
    /// </summary>
    public static class AwareMessageBox
    {
        public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options)
        {
            if (IsRightToLeft(owner))
            {
                options |= MessageBoxOptions.RtlReading |
                MessageBoxOptions.RightAlign;
            }
            return MessageBox.Show(owner, text, caption,
            buttons, icon, defaultButton, options);
        }
        private static bool IsRightToLeft(IWin32Window owner)
        {
            Control control = owner as Control;

            if (control != null)
            {
                return control.RightToLeft == RightToLeft.Yes;
            }

            // If no parent control is available, ask the CurrentUICulture
            // if we are running under right-to-left.
            return CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
        }
    }

    public static class Tools
    {
        /// <summary>
        /// Возвращается основная форма приложения
        /// </summary>
        /// <returns>Основная форма приложения</returns>
        public static Form MainForm
        {
            get { return Application.OpenForms[0]; }
        }
        /// <summary>
        /// Открытые формы документов
        /// </summary>
        /// <returns></returns>
        public static Form[] DocumentsForm()
        {
            return (from Form form in Application.OpenForms where form.Name == "FormDocuments" select form).ToArray(); 
        }

        public static DateTime DateAndCurrentTime(DateTime dateTime)
        {
            int year = dateTime.Year;
            int month = dateTime.Month;
            int day = dateTime.Day;
            DateTime dt = DateTime.Now;
            int hour = dt.Hour;
            int min = dt.Minute;
            int sec = dt.Second;
            return new DateTime(year, month, day, hour, min, sec);
        }

        /// <summary>
        /// Открывает дочернюю форму
        /// </summary>
        /// <param name="formChild">Ссыка на дочернюю форму</param>
        /// <param name="formParent">Ссылка на родительску форму</param>
        public static void OpenMdiForm(Form formChild, Form formParent)
        {
            formChild.MdiParent = formParent;
            formChild.StartPosition = FormStartPosition.Manual;
            formChild.Show();
            formChild.Activate();
        }

        public static void OpenDialogMdiForm(Form formChild, Form formParent)
        {
            if (Tools.IsMdiFormOpen(formChild.Name))
            {
                Application.OpenForms[formChild.Name].Activate();
                formChild.Close();
                return;
            } 
            formChild.MdiParent = formParent;
            formChild.StartPosition = FormStartPosition.Manual;
            formChild.Location = new Point((formParent.Width - formChild.Width) / 2, (formParent.Height - formChild.Height) / 2);
            formChild.Show();
        }


        /// <summary>
        /// Отображает для контрола всплывающую подсказку. Подсказка указывает на середину контрола.
        /// </summary>
        /// <param name="control">Контрол, для которого выводится подсказка</param>
        /// <param name="title">Название подсказки</param>
        /// <param name="text">Текст подсказки</param>
        /// <param name="toolTypeIcon">Иконка подсказки</param>
        /// <param name="duration">Длительность отображения в миллисекундах, если задано значение 0 - подсказка отображается до тех пор, пока пользователь на нее не кликнет</param>
        public static void ShowTip(Control control, string title, string text, ToolTipIcon toolTypeIcon, int duration)
        {
            ToolTip toolTip = new ToolTip();
            toolTip.ToolTipIcon = toolTypeIcon;
            toolTip.BackColor = SystemColors.Window;
            toolTip.ToolTipTitle = title;
            //toolTip.IsBalloon = true;
            toolTip.Active = true;
            toolTip.Show(text, control, control.Width / 2, control.Height / 2, 800);
        }
        /// <summary>
        /// Удаляет все элементы списка из комбобокса, добавляет значение "Нет данных" (id = -1), и позиционируется на него
        /// </summary>
        public static void ClearComboBox(ComboBox comboBox)
        { 
            if (comboBox != null)
            {
                comboBox.Items.Clear();
                comboBox.Items.Add(new ComboBoxItem(-1, CommonText.ComboBox_Empty));
                if (comboBox.Items.Count > 0)
                    comboBox.SelectedIndex = 0;
            }
            
        }
        /// <summary>
        /// Определяет, открыта ли форма с выбранным именем
        /// </summary>
        /// <param name="formName">Имя формы</param>
        /// <returns>Возаращается true, если форма открыта, false - если не открыта</returns>
        public static bool IsMdiFormOpen(string formName)
        {
            return (from Form applicationForm in Application.OpenForms
                    where applicationForm.Name == formName
                    select applicationForm).Count() > 0;
        }
        /// <summary>
        /// Вывод сообщения с учетом возможности использования языков с написанием справо-налево
        /// </summary>
        /// <param name="a_text">Текст сообщения</param>
        /// <returns>Выбранная пользователем кнопка закрытия диалоговой формы</returns>
        public static DialogResult ShowMessage(string a_text)
        {
            return AwareMessageBox.Show(null, a_text, Application.ProductName, MessageBoxButtons.OKCancel,
                MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }
        /// <summary>
        /// Очистка значений списка, без удаления элементов 
        /// </summary>
        /// <param name="listView">Очищаемый список</param>
        public static void ClearListViewText(ListView listView)
        {
            for (int i = 0; i < listView.Items.Count; i++)
            {
                listView.Items[i].Text = "";
                for (int j = 0; j < listView.Items[i].SubItems.Count; j++)
                {
                    listView.Items[i].SubItems[j].Text = "";
                    listView.Items[i].SubItems[j].BackColor = Color.FromKnownColor(KnownColor.Window);
                }
            }
            
        }
        /// <summary>
        /// Преобразует входящий объект в строку, если объект null - возвращается пустая строка
        /// </summary>
        /// <param name="obj">Объект преобразования</param>
        /// <returns>Строка преобразования</returns>
        public static string AsNullString(object obj)
        {
            if (obj == null)
                return "";
            else
                return obj.ToString();
        }
        public static bool IsNullString(string str)
        {
            return str.Trim() == "" ? true : false;
        }
        /// <summary>
        /// Создается строковый объект DataTable по переданному заголовку и списку данных.
        /// Нужно помнить что количество столбцов в параметре header и
        /// количество элементов в массиве string[] параметра tableRows - должно совпадать
        /// </summary>
        /// <param name="headers">Массив строк из которых будет построен заголовок таблиц</param>
        /// <param name="tableRows">Спиосок массива строк, которые будут использованы для заполнения таблицы</param>
        /// <returns>Заполненный строками объект DataTable</returns>
        public static DataTable FillTable(string[] headers, List<string[]> tableRows)
        {
            DataTable dataTable = new DataTable();
            for (int i = 0; i < headers.Count(); i++)
                dataTable.Columns.Add(new DataColumn(headers[i], System.Type.GetType("System.String")));
            foreach (string[] tableCells in tableRows)
            {
                DataRow dataRow = dataTable.NewRow();
                for (int i = 0; i < tableCells.Count(); i++)
                    dataRow[i] = tableCells[i];
                dataTable.Rows.Add(dataRow);
            }
            return dataTable;
        }
        /// <summary>
        /// Метод вызова справки
        /// </summary>
        /// <param name="topic">Связанныая тема</param>
        public static void GetHelp(string topic, HelpNavigator helpNavigator)
        {
            try
            {
               Help.ShowHelp(null, Path.ChangeExtension(Application.ExecutablePath, ".chm"), helpNavigator, topic + ".htm");
            }
            catch (Exception err)
            {
                CashCommon.ECash.ShowError(err.Message); 
            }
        }
        /// <summary>
        /// Вычисляет значение НДС и денег без НДС, по полной сумме
        /// </summary>
        /// <param name="amount"></param>
        /// <param name="tax"></param>
        /// <returns>1 элемент массива содержит НДС, второй - сумму без НДС</returns>
        public static PayNDS GetTax(double amount, double tax)
        {
            PayNDS payNDS = new PayNDS();
            payNDS.Value = amount /(1+ tax / 100.0);
            payNDS.Tax = amount - payNDS.Value;
            return payNDS;
        }
        /// <summary>
        /// Вызов блокнота с записями лога
        /// </summary>
        public static void GetLogFile()
        {
            string pathFile = String.Format("{0}\\{1}", Adjustment.AppDir, "Cash.log");
            try
            {
                Process.Start("notepad.exe", pathFile);
            }
            catch
            {
                throw new ECash(MessageType.UserMessage, String.Format(CommonError.FileNotFound, pathFile), "");
            }
        }
        /// <summary>
        /// Вызов блокнота с записями конфигурационного файла
        /// </summary>
        public static void GetConfigFile()
        {
            string pathFile = String.Format("{0}\\{1}", Adjustment.AppDir, "Cash.exe.config");
            try
            {
                Process.Start("notepad.exe", pathFile);
            }
            catch
            {
                throw new ECash(MessageType.UserMessage, String.Format(CommonError.FileNotFound, pathFile), "");
            }
        }
    }

    public struct PayNDS
    {
        public double Tax {get; set;}
        public double Value {get; set;}
    }

    public sealed class WinApi
    {
        public const int WM_SCROLL = 276; // Horizontal scroll
        public const int WM_VSCROLL = 277; // Vertical scroll
        public const int SB_LINEUP = 0; // Scrolls one line up
        public const int SB_LINELEFT = 0;// Scrolls one cell left
        public const int SB_LINEDOWN = 1; // Scrolls one line down
        public const int SB_LINERIGHT = 1;// Scrolls one cell right
        public const int SB_PAGEUP = 2; // Scrolls one page up
        public const int SB_PAGELEFT = 2;// Scrolls one page left
        public const int SB_PAGEDOWN = 3; // Scrolls one page down
        public const int SB_PAGERIGTH = 3; // Scrolls one page right
        public const int SB_PAGETOP = 6; // Scrolls to the upper left
        public const int SB_LEFT = 6; // Scrolls to the left
        public const int SB_PAGEBOTTOM = 7; // Scrolls to the upper right
        public const int SB_RIGHT = 7; // Scrolls to the right
        public const int SB_ENDSCROLL = 8; // Ends scroll     
        public const int SB_THUMBPOSITION = 4; // Scroll to absolute position
        public const int SB_THUMBTRACK = 5; 
        
        [DllImport("user32")]
        public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
        
        [DllImport("user32")]
        public static extern int SetParent(int hWndChild, int hWndNewParent);
    }

    public struct PostResult
    {
        public DateTime DatePost { get; set; }
        public Double Sum { get; set; }
        public int Count { get; set; }
    }

    #region Делегаты

        public delegate void DlegateWithoutParams();
        public delegate void DelegateDataFromDataTo(DateTime dtFrom, DateTime dtTo);
        public delegate void DelegateFormResult(int id);

    #endregion
}
